/*
 * Interface con FUSE
 *
 * Acá se definen todas las funciones que maneja FUSE. Estas
 * llaman a las funciones de filesystem.h para manejar el
 * disco.
 *
 * =)
 */

#define FUSE_USE_VERSION 26
#define _FILE_OFFSET_BITS 64
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>

#include "collections/list.h"
#include "filesystem.h"
#include "inicializacion.h"
#include "directorios.h"
#include "archivos.h"
#include "clusters.h"
#include "comandos_consola.h"


static int fuse_getattr(const char *path, struct stat *stbuf)
{
	return set_getattr(stbuf, path);

}

static int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                       off_t offset, struct fuse_file_info *fi)
{
	t_link_element *element;
	OPENFILE *data = NULL;

	t_list* list;

	list = getDirectorioTD(path);

	// El directorio raiz no tiene las entradas de . y ..
	if(!strcmp(path,"/")) {
		filler(buf, ".", NULL, 0);
		filler(buf, "..", NULL, 0);
	}

	element = list->head;

	while( element != NULL ){
		data = element->data;

//		static int * set_getattr(t_stat *st,const char *path);

		filler(buf, data->fileName , NULL, 0);

		element = element->next;

	}

// destroy list
	return 0;

}

/*
static int fuse_mkdir(const char *path, mode_t mode)
{
        int res;

        res = mkdir(path, mode);
        if (res == -1)
                return -errno;

        return 0;
}

static int fuse_unlink(const char *path)
{
        int res;

        res = unlink(path);
        if (res == -1)
                return -errno;

        return 0;
}

static int fuse_rmdir(const char *path)
{
        int res;

        res = rmdir(path);
        if (res == -1)
                return -errno;

        return 0;
}

static int fuse_rename(const char *from, const char *to)
{
        int res;

        res = rename(from, to);
        if (res == -1)
                return -errno;

        return 0;
}

static int fuse_truncate(const char *path, off_t size)
{
        int res;

        res = truncate(path, size);
        if (res == -1)
                return -errno;

        return 0;
}
*/
static int fuse_ftruncate(const char *path, off_t size,
                         struct fuse_file_info *fi)
{
        int res;

        (void) path;

        //res = ftruncate(fi->fh, size);

        truncarArchivo(fi->fh,size);

        if (res == -1)
                return -errno;

        return 0;
}
/*
static int fuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
        int fd;

        fd = open(path, fi->flags, mode);
        if (fd == -1)
                return -errno;

        fi->fh = fd;
        return 0;
}
*/
static int fuse_open(const char *path, struct fuse_file_info *fi)
{
        uint64_t fd; // Descriptor del archivo

        fd = abrirArchivo(path);

        if (fd == -1)
        	return -2;

        fi->fh = fd;
        return 0;
}

static int fuse_read(const char *path, char *buf, size_t size, off_t offset,
                    struct fuse_file_info *fi)
{
		int ret_size;

		if(!archivoAbierto(fi->fh)) {
        	return -ESPIPE;
		}

		ret_size = leerArchivo(buf, fi->fh, size, offset);

        return size;
}

static int fuse_write(const char *path, const char *buf, size_t size,
                     off_t offset, struct fuse_file_info *fi)
{
        int res;

        if(!archivoAbierto(fi->fh)) {
			return -ESPIPE;
		}

        escribirArchivo(fi->fh, size, offset, (void*)buf);

        return size;
}

static int fuse_flush(const char *path, struct fuse_file_info *fi)
{
        int res;

        (void) path;

        OPENFILE* tdArchivo = (OPENFILE*) fi->fh;
        uint32_t i;
        uint32_t cache_max = configuracion.tamanio_cache / (cache_boot_sector->n_sectors_per_cluster * SECTOR_SIZE);

        tdArchivo->vecesAbierto--;

        for(i=0;i<cache_max;i++){

			if (tdArchivo->cache[i].buf != NULL){
				if(tdArchivo->cache[i].modificado == 1){
					escribirCluster(tdArchivo->cache[i].Cluster,tdArchivo->cache[i].buf);

				}
				free(tdArchivo->cache[i].buf);
	        	tdArchivo->cache[i].orden   = 0;
	        	tdArchivo->cache[i].Cluster = 0;
	        	tdArchivo->cache[i].modificado = 0;
	        	tdArchivo->cache[i].buf = NULL;

			}

		}

        return 0;
}

static int fuse_release(const char *path, struct fuse_file_info *fi)
{
        (void) path;

        cerrarArchivo(fi->fh);

        return 0;
}

static struct fuse_operations fuse_oper = {
        .getattr        = fuse_getattr,
        .readdir        = fuse_readdir,
        //.mkdir        = fuse_mkdir,
        //.unlink       = fuse_unlink,
        //.rmdir        = fuse_rmdir,
        //.rename       = fuse_rename,
        //.truncate     = fuse_truncate,
        .ftruncate    = fuse_ftruncate,
        //.create       = fuse_create,
        .open           = fuse_open,
        .read         	= fuse_read,
        .write          = fuse_write,
        .flush        = fuse_flush,
        .release      = fuse_release
};

typedef struct {
	int argc;
	char **argv;
} ARGUMENTOS;

void thread_fuse(ARGUMENTOS* argumentos){

	int argc = argumentos->argc;
	char** argv = argumentos->argv;
	int res;

	free(argumentos);

	res = fuse_main(argc, argv, &fuse_oper, NULL);
	if(!res) {
			perror("fuse_main");
			exit(0);
	}
	printf("Fuse finalizado\n");
}

int main(int argc, char **argv) {

	ARGUMENTOS* argumentos;
	pthread_t t_id;

	signal(SIGUSR1, dump_cache);

	printf("Cargando archivo de configuración... ");
	leerConfig(PATH_CONFIG,&configuracion);
	printf("cargado\n");

	printf("Conectando con el RAID o el Planificador de disco... ");
	conectarClientes(configuracion.max_conexiones);
	printf("Conectado.\n");

	leerBootSector();
	printf("Se leyó el boot sector\n");
	/*
	printf("Bytes per sector: %u\n", cache_boot_sector->n_bytes_per_sector);
	printf("Sectores por cluster:%u\n", cache_boot_sector->n_sectors_per_cluster);
	printf("Reserved sectors: %u\n", cache_boot_sector->n_reserved_sector);
	printf("Sectors per fat: %u\n", cache_boot_sector->n_sectors_per_fat);
	printf("Cant sectors: %u\n", cache_boot_sector->n_sectors_ext);
	printf("Cluster root dir: %u\n", cache_boot_sector->n_cluster_root_dir);
	 */
	leerFat();
	printf("Se leyó la fat\n");

	ObtenerClusterLibres();
	printf("Obtenida lista de %u clusters libres\n", ptrListaSectLibre->elements_count);

	argumentos = malloc(sizeof(ARGUMENTOS));
	argumentos->argc = argc;
	argumentos->argv = argv;

	pthread_create(&t_id, NULL, (void*)&thread_fuse, (void*) argumentos);

	iniciarConsola("Filesystem", 1, consola_handler);

	/*
	//liberarLista(ptrListaSectLibre);
	free(puntero);
	free(cache_boot_sector);
	free(cache_fat);
	free(sector_buffer);
	collection_queue_clean(ptrListaSectLibre,destructor_cluster);*/


	return 0;
}
